'''
Created on May 3, 2010

@author: Juan

Based on this article: http://code.activestate.com/recipes/496786-simple-xml-rpc-server-over-https/

Requires that pyOpenSSL be installed
'''

LISTEN_HOST='' # You should not use '' here, unless you have a real FQDN.
LISTEN_PORT=443

KEY_FILE='certs/keyfile.pem'    # Replace with your PEM formatted key file
CERT_FILE='certs/certfile.pem'  # Replace with your PEM formatted certificate file


import SocketServer
import BaseHTTPServer
import SimpleHTTPServer
import SimpleXMLRPCServer

import socket, os
from OpenSSL import SSL

class SecureXMLRPCServer(BaseHTTPServer.HTTPServer, SimpleXMLRPCServer.SimpleXMLRPCDispatcher):
    def __init__(self, server_address, HandlerClass, logRequests=True):
        """Secure XML-RPC server.
        
        It is very similar to SimpleXMLRPCServer but it uses HTTPS for transproting XML data.
        """
        self.logRequests = logRequests
        
        try:
            SimpleXMLRPCServer.SimpleXMLRPCDispatcher.__init__(self)
        except TypeError:
            # An exception is raised in Python 2.5 as the prototype of the __init__
            # method has changed and now has 3 arguments (self, allow_none, encoding)
            SimpleXMLRPCServer.SimpleXMLRPCDispatcher.__init__(self, False, None)
            
        SocketServer.BaseServer.__init__(self, server_address, HandlerClass)
        context = SSL.Context(SSL.SSLv23_METHOD)
        context.use_privatekey_file(KEY_FILE)
        context.use_certificate_file(CERT_FILE)
        self.socket = SSL.Connection(context, socket.socket(self.address_family,
                                                            self.socket_type))
        self.server_bind()
        self.server_activate()
        
class SecureXMLRPCRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
    """Secure XML-RPC request handler class.
    
    It is very similar to SimpleXMLRPCRequestHandler but it uses HTTPS for transporting XML data.
    """
    def setup(self):
        self.connection = self.request
        self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
        self.wfile = socket._fileobject(self.request, "wb", self.rbufsize)
        
    def do_POST(self):
        """Handles the HTTPS POST request.
        
        It was copied out from SimpleXMLRPCServer.py and modified to shutdown the socket cleanly
        """
        try:
            # get arguments
            data = self.rfile.read(int(self.headers["content-length"]))
            # In previous versions of SimpleXMLRPCServer, _dispatch
            # could be overridden in this class, instead of in
            # SimpleXMLRPCDispatcher. To maintain backwards compatibility,
            # check to see if a subclass implements _dispatch and dispatch
            # using that method if present.
            response = self.server._marshaled_dispatch(data,
                                                       getattr(self, '_dispatch', None)
                                                       )
        except: # This should only happen if the module is buggy
            # internal error, report as HTTP server error
            self.send_response(500)
            self.end_headers()
        else:
            # got a valid XML RPC reponse
            self.send_response(200)
            self.send_header("Content-type", "text/xml")
            self.send_header("Content-length", str(len(response)))
            self.end_headers()
            self.wfile.write(response)
            
            # shut down the connection
            self.wfile.flush()
            self.connection.shutdown()
            
def test(HandlerClass = SecureXMLRPCRequestHandler, ServerClass = SecureXMLRPCServer):
    """Text XML-RPC over HTTPS server"""
    class xmlrpc_registers:
        def __init__(self):
            import string
            self.python_string = string
            
        def add(self, x, y):
            return x + y
        
        def mult(self, x, y):
            return x * y
        
        def div(self, x, y):
            return x / y
        
        def sub(self, x, y):
            return x - y
    
    server_address = (LISTEN_HOST, LISTEN_PORT)
    server = ServerClass(server_address, HandlerClass)
    server.register_instance(xmlrpc_registers())
    sa = server.socket.getsockname()
    print "Serving HTTPS on", sa[0], "port", sa[1]
    server.serve_forever()
    
if __name__ == "__main__":
    test()